<?php
// @formatter:off
/**
 * @file WCDBChecks.php
 * @author Alejandro Dario Simi
 * @date $Date: 2014-05-04 01:33:52 +0000 (Sun, 04 May 2014) $
 *
 * $Id: WCDBChecks.php 111 2014-05-04 01:33:52Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/trunk/includes/db/WCDBChecks.php $
 */
// @formatter:on

/**
 * @class WCDBCheck
 */
class WCDBCheck {
	/**
	 * @var WCConnection Point to the database connection singleton.
	 */
	protected $_db = false;
	/**
	 * @var string Tables prefixes.
	 */
	protected $_dbprefix = "";
	/**
	 * @var SimpleXMLElement @todo
	 */
	protected $_xml = false;
	/**
	 * @var boolean @todo
	 */
	protected $_checksOut = null;

	public function __construct(SimpleXMLElement &$xml, $db, $dbprefix) {
		$this->_db = $db;
		$this->_dbprefix = $dbprefix;
		$this->_xml = $xml;
	}
	//
	// Public methods.
	public function actions() {
		$out = array();

		foreach($this->_xml->ck_action as $xQuery) {
			$out[] = $this->cleanQuery("{$xQuery}");
		}

		return $out;
	}
	public function check() {
		if($this->_checksOut === null) {
			$this->_checksOut = false;

			$query = $this->cleanQuery("{$this->_xml->ck_condition}");
			$query = "select count(*) as c from ($query) t";

			$res = $this->_db->query($query, false);
			if($res) {
				$row = $res->fetch();
				$this->_checksOut = $row["c"] > 0;
				$res->closeCursor();
			} else {
				trigger_error(__CLASS__."::".__FUNCTION__."(): Check query failed. Query: '{$query}'", E_USER_ERROR);
			}
		}
		return $this->_checksOut;
	}
	//
	// Protected methods.
	protected function cleanQuery($query) {
		$query = str_replace(array(
			"\n",
			"\t"
		), " ", $query);
		$query = str_replace("%prefix%", $this->_dbprefix, $query);
		while(strpos($query, "  ") !== false) {
			$query = str_replace("  ", " ", $query);
		}

		return $query;
	}
}

/**
 * @class WCDBChecks
 */
class WCDBChecks {
	/**
	 * @var WCConnection Point to the database connection singleton.
	 */
	protected $_db = false;
	/**
	 * @var string Tables prefixes.
	 */
	protected $_dbprefix = "";
	/**
	 * @var SimpleXMLElement @todo
	 */
	protected $_xml = false;
	/**
	 * @var boolean @todo
	 */
	protected $_loaded = false;
	/**
	 * @var WCDBCheck[string][string][string] @todo
	 */
	protected $_columns = array(
		"add" => array(),
		"drop" => array(),
		"modify" => array()
	);
	/**
	 * @var WCDBCheck[string][string] @todo
	 */
	protected $_table = array(
		"create" => array(),
		"drop" => array()
	);

	public function __construct(SimpleXMLElement &$xml, $db, $dbprefix) {
		$this->_db = $db;
		$this->_dbprefix = $dbprefix;
		$this->_xml = $xml;
	}
	//
	// Public methods.
	public function columnAddAfter($column) {
		return $this->columnsActions("add", "after", $column);
	}
	public function columnAddBefore($column) {
		return $this->columnsActions("add", "before", $column);
	}
	public function columnDropAfter($column) {
		return $this->columnsActions("drop", "after", $column);
	}
	public function columnDropBefore($column) {
		return $this->columnsActions("drop", "before", $column);
	}
	public function columnModifyAfter($column) {
		return $this->columnsActions("modify", "after", $column);
	}
	public function columnModifyBefore($column) {
		return $this->columnsActions("modify", "before", $column);
	}
	public function createAfter() {
		return $this->tableActions("create", "after");
	}
	public function createBefore() {
		return $this->tableActions("create", "before");
	}
	public function dropAfter() {
		return $this->tableActions("drop", "after");
	}
	public function dropBefore() {
		return $this->tableActions("drop", "before");
	}
	//
	// Protected methods.
	protected function addColumnChecks($type, SimpleXMLElement &$xml) {
		foreach($xml as $xValue) {
			$column = "{$xValue->columnname}";

			$this->_columns[$type][$column] = array(
				"before" => array(),
				"after" => array()
			);

			if(isset($xValue->ck_before)) {
				foreach($xValue->ck_before as $xCheck) {
					$this->_columns[$type][$column]["before"][] = new WCDBCheck($xCheck, $this->_db, $this->_dbprefix);
				}
			}
			if(isset($xValue->ck_after)) {
				foreach($xValue->ck_after as $xCheck) {
					$this->_columns[$type][$column]["after"][] = new WCDBCheck($xCheck, $this->_db, $this->_dbprefix);
				}
			}

		}
	}
	protected function addTableChecks($type, SimpleXMLElement &$xml) {
		foreach($xml as $xValue) {
			$this->_table[$type] = array(
				"before" => array(),
				"after" => array()
			);

			if(isset($xValue->ck_before)) {
				foreach($xValue->ck_before as $xCheck) {
					$this->_table[$type]["before"][] = new WCDBCheck($xCheck, $this->_db, $this->_dbprefix);
				}
			}
			if(isset($xValue->ck_after)) {
				foreach($xValue->ck_after as $xCheck) {
					$this->_table[$type]["after"][] = new WCDBCheck($xCheck, $this->_db, $this->_dbprefix);
				}
			}

		}
	}
	protected function columnsActions($type, $when, $column) {
		$out = array();

		$this->load();

		if(isset($this->_columns[$type][$column])) {
			$this->columnsFilter($type, $column);

			foreach($this->_columns[$type][$column][$when] as $checker) {
				foreach($checker->actions() as $query) {
					$out[] = $query;
				}
			}
		}

		return $out;
	}
	protected function columnsFilter($type, $column) {
		if(isset($this->_columns[$type][$column]) && !isset($this->_columns[$type][$column]["filtered"])) {
			foreach($this->_columns[$type][$column]["before"] as $key => $check) {
				if(!$check->check()) {
					unset($this->_columns[$type][$column]["before"][$key]);
				}
			}
			foreach($this->_columns[$type][$column]["after"] as $key => $check) {
				if(!$check->check()) {
					unset($this->_columns[$type][$column]["after"][$key]);
				}
			}
			$this->_columns[$type][$column]["filtered"] = "filtered";
		}
	}
	protected function load() {
		if(!$this->_loaded) {
			if(isset($this->_xml->ck_creation)) {
				$this->addTableChecks("create", $this->_xml->ck_creation);
			}
			if(isset($this->_xml->ck_drop)) {
				$this->addTableChecks("drop", $this->_xml->ck_drop);
			}
			if(isset($this->_xml->ck_columnadd)) {
				$this->addColumnChecks("add", $this->_xml->ck_columnadd);
			}
			if(isset($this->_xml->ck_columndrop)) {
				$this->addColumnChecks("drop", $this->_xml->ck_columndrop);
			}
			if(isset($this->_xml->ck_columnmodify)) {
				$this->addColumnChecks("modify", $this->_xml->ck_columnmodify);
			}

			$this->_loaded = true;
		}
	}
	protected function tableActions($type, $when) {
		$out = array();

		$this->load();

		if(isset($this->_table[$type])) {
			$this->tableFilter($type);

			foreach($this->_table[$type][$when] as $checker) {
				foreach($checker->actions() as $query) {
					$out[] = $query;
				}
			}
		}

		return $out;
	}
	protected function tableFilter($type) {
		if(isset($this->_table[$type]) && !isset($this->_table[$type]["filtered"])) {
			foreach($this->_table[$type]["before"] as $key => $check) {
				if(!$check->check()) {
					unset($this->_table[$type]["before"][$key]);
				}
			}
			foreach($this->_table[$type]["after"] as $key => $check) {
				if(!$check->check()) {
					unset($this->_table[$type]["after"][$key]);
				}
			}
			$this->_table[$type]["filtered"] = "filtered";
		}
	}
}
